/**
 * FormItem 组件 - 表单字段项组件
 *
 * 功能概述：
 * 1. 负责单个表单字段的完整生命周期管理
 * 2. 通过 React.cloneElement 自动为子组件注入 value 和 onChange 等属性
 * 3. 管理字段的验证状态和错误信息显示
 * 4. 与 Form 组件通过 Context 进行状态同步
 * 5. 支持多种类型的输入组件（Input、Select、Checkbox 等）
 *
 * 核心特性：
 * - 自动属性注入：自动为子组件添加 value、onChange 等属性
 * - 验证管理：基于 async-validator 的验证规则
 * - 错误显示：自动显示验证错误信息
 * - 灵活配置：支持自定义值提取、验证触发时机等
 */

import React, { FC, ReactNode, useContext, useEffect } from "react";
import classNames from "classnames";
import { FormContext } from "./form";
import { CustomRule } from "./useStore";

/**
 * 部分必填类型工具
 * 确保类型 T 中的某些属性 K 有默认值，其他属性保持可选
 *
 * 使用场景：
 * 为 FormItem 组件的某些属性提供默认值，同时保持其他属性可选
 */
export type SomeRequired<T, K extends keyof T> = Required<Pick<T, K>> &
  Omit<T, K>;

/**
 * FormItem 组件属性接口
 * 定义了单个表单字段项的所有配置选项
 */
export interface FormItemProps {
  /** 字段名，对应表单数据中的 key，必须唯一 */
  name: string;
  /** label 标签的文本，用于显示字段名称 */
  label?: string;
  /** 子组件，通常是输入组件如 Input、Select、Checkbox 等 */
  children?: ReactNode;
  /**
   * 子节点的值的属性名
   * 不同输入组件有不同的值属性，如：
   * - Input: 'value'
   * - Checkbox: 'checked'
   * - Select: 'value'
   */
  valuePropName?: string;
  /**
   * 设置收集字段值变更的时机
   * 如 'onChange'、'onBlur'、'onFocus' 等
   */
  trigger?: string;
  /**
   * 设置如何从事件对象中提取字段值
   * 默认：e => e.target.value（适用于标准输入框）
   * 自定义：如 checkbox 可以使用 e => e.target.checked
   */
  getValueFromEvent?: (event: any) => any;
  /**
   * 校验规则数组，设置字段的校验逻辑
   * 基于 async-validator 库，支持同步和异步验证
   */
  rules?: CustomRule[];
  /**
   * 设置字段校验的触发时机
   * 如 'onBlur'（失焦时验证）、'onChange'（值变化时验证）
   */
  validateTrigger?: string;
  /** 帮助文本，显示在字段下方 */
  help?: string;
  /** 标签宽度，覆盖 Form 组件的 labelWidth 设置 */
  labelWidth?: number;
}

/**
 * FormItem 组件主函数
 *
 * 实现细节：
 * 1. 使用 React Context 获取表单状态和方法
 * 2. 在组件挂载时初始化字段状态
 * 3. 使用 React.cloneElement 为子组件注入属性
 * 4. 管理字段的验证状态和错误显示
 * 5. 支持动态 CSS 类名计算
 *
 * @param props - FormItem 组件属性
 * @returns FormItem 组件 JSX
 */
export const FormItem: FC<FormItemProps> = (props) => {
  /**
   * 解构 props，使用 SomeRequired 类型确保某些属性有默认值
   * 这样即使外部没有传递这些属性，内部也有默认值可用
   */
  const {
    label,
    children,
    name,
    valuePropName = "value",
    trigger = "onChange",
    getValueFromEvent = (e) => e.target.value,
    rules,
    validateTrigger = "onBlur",
    help,
    labelWidth,
  } = props;

  /**
   * 从 Form Context 获取表单状态管理方法
   * 这些方法用于与 Form 组件进行状态同步
   */
  const { 
    dispatch, 
    fields, 
    initialValues, 
    validateField,
    layout,
    labelWidth: formLabelWidth,
    labelAlign 
  } = useContext(FormContext);

  /**
   * 动态计算 CSS 类名
   * 根据组件状态动态生成相应的样式类
   */
  const rowClass = classNames("viking-row", {
    "viking-row-no-label": !label, // 没有 label 时的特殊样式
  });

  /**
   * 组件挂载时初始化字段
   *
   * 处理流程：
   * 1. 从初始值中获取当前字段的值
   * 2. 分发添加字段的 action 到表单状态管理
   * 3. 初始化字段的验证状态
   *
   * 注意：使用空依赖数组，只在组件挂载时执行一次
   */
  useEffect(() => {
    // 从初始值中获取当前字段的值，如果不存在则使用空字符串
    const value = (initialValues && initialValues[name]) || "";

    /**
     * 分发添加字段的 action
     * 将字段信息添加到表单的全局状态管理中
     */
    dispatch({
      type: "addField",
      name,
      value: {
        label, // 字段标签
        name, // 字段名
        value, // 字段值
        rules: rules || [], // 验证规则，默认为空数组
        errors: [], // 错误信息，初始为空
        isValid: true, // 验证状态，初始为通过
      },
    });
  }, []); // 空依赖数组，只在组件挂载时执行一次

  /**
   * 从表单状态中获取当前字段的状态
   * 如果字段尚未初始化，fieldState 可能为 undefined
   */
  const fieldState = fields[name];
  const value = fieldState && fieldState.value; // 字段当前值
  const errors = fieldState && fieldState.errors; // 字段验证错误信息

  /**
   * 计算字段是否必填
   * 检查 rules 中是否有 required 规则
   * 注意：函数类型的规则需要运行时判断，这里只检查普通规则
   */
  const isRequired = rules?.some(
    (rule) => typeof rule !== "function" && rule.required
  );

  /**
   * 判断字段是否有错误
   * 错误信息数组长度大于0表示有错误
   */
  const hasError = errors && errors.length > 0;

  /**
   * 动态计算标签的 CSS 类名
   * 根据字段状态动态生成相应的样式类
   */
  const labelClass = classNames({
    "viking-form-item-required": isRequired, // 必填字段的星号样式
  });

  /**
   * 动态计算表单项的 CSS 类名
   * 根据验证状态动态生成相应的样式类
   */
  const itemClass = classNames("viking-form-item-control", {
    "viking-form-item-has-error": hasError, // 错误状态下的样式
  });

  /**
   * 字段值更新处理函数
   *
   * 处理流程：
   * 1. 从事件对象中提取字段值
   * 2. 更新字段值到表单状态中
   *
   * @param e - 事件对象
   */
  const onValueUpdate = (e: any) => {
    const value = getValueFromEvent(e); // 使用自定义值提取函数
    console.log("new value", value); // 开发调试日志
    dispatch({ type: "updateValue", name, value }); // 分发更新值的 action
  };

  /**
   * 字段验证处理函数
   * 触发当前字段的验证逻辑
   */
  const onValueValidate = async () => {
    await validateField(name); // 调用表单的验证方法
  };

  /**
   * 步骤1：手动创建要注入给子组件的属性列表
   * 这些属性将自动添加到子组件上
   */
  const controlProps: Record<string, any> = {};
  controlProps[valuePropName] = value; // 注入值属性，如 value 或 checked
  controlProps[trigger] = onValueUpdate; // 注入值变更处理函数，如 onChange

  /**
   * 如果有验证规则，注入验证触发属性
   * 这样子组件可以在特定时机触发验证
   */
  if (rules) {
    controlProps[validateTrigger] = onValueValidate; // 如 onBlur
  }

  /**
   * 步骤2：获取 children 数组的第一个元素
   * FormItem 只支持单个子组件
   */
  const childList = React.Children.toArray(children);

  /**
   * 子组件数量检查 - 没有子组件
   * 输出错误信息，提醒开发者提供表单组件
   */
  if (childList.length === 0) {
    console.error(
      "No child element found in Form.Item, please provide one form component"
    );
  }

  /**
   * 子组件数量检查 - 子组件大于一个
   * 输出警告信息，只使用第一个子组件
   */
  if (childList.length > 1) {
    console.warn(
      "Only support one child element in Form.Item, others will be omitted"
    );
  }

  /**
   * 子组件类型检查 - 不是 ReactElement
   * 输出错误信息，提醒提供有效的 React 组件
   */
  if (!React.isValidElement(childList[0])) {
    console.error("Child component is not a valid React Element");
  }

  /**
   * 获取第一个有效的子组件
   * 如果子组件列表为空，使用空元素作为占位符
   */
  const child = childList[0] as React.ReactElement;

  /**
   * 步骤3：使用 React.cloneElement 混合子组件原有属性和新注入的属性
   * 这是实现自动属性注入的核心技术
   */
  const returnChildNode = React.cloneElement(
    child,
    { ...(child.props as any), ...controlProps } // 合并原有属性和新属性
  );

  /**
   * 返回 FormItem 组件的 JSX
   * 包含标签区域、表单项区域和错误信息区域
   */
  return (
    <div className={rowClass}>
      {/*
       * 渲染标签区域
       * 只有当 label 存在时才渲染标签
       */}
      {label && (
        <div className="viking-form-item-label">
          <label title={label} className={labelClass}>
            {label}
          </label>
        </div>
      )}

      {/*
       * 渲染表单项区域
       * 包含经过属性注入的子组件
       */}
      <div className="viking-form-item">
        <div className={itemClass}>{returnChildNode}</div>
      </div>

      {/*
       * 渲染错误信息区域
       * 只有当存在错误时才显示错误信息
       */}
      {hasError && (
        <div className="viking-form-item-explain">
          <span>
            {errors[0].message} {/* 显示第一个错误信息 */}
          </span>
        </div>
      )}

      {/*
       * 渲染帮助文本区域
       * 只有当 help 存在且没有错误时才显示帮助文本
       */}
      {help && !hasError && (
        <div className="viking-form-item-help">
          <span>{help}</span>
        </div>
      )}
    </div>
  );
};



export default FormItem;
